include('shared.lua')

ENT.RenderGroup = RENDERGROUP_OPAQUE
PlaybackDataActive = false
PreviewCameraData = nil

function ENT:Initialize()
	self.PreviewFrac = 0.0
	self.DirectionDir = Vector(0,0,10)
	self.DirectionPos = Vector(10,0,0) + self:GetPos()
	self.ShakeAngles = Angle(0,0,0)
	self.ShakeTimer = 0.0
	self.ActorSequences = {}
end

function ENT:Lerp(dataA, dataB, frac)

	return (dataA + ((dataB - dataA) * frac))

end

function ENT:ThinkPlayback()

end


local gControlPanel = nil
local gTrackname = nil
local gOnNode = nil
local gTotalNodes = nil
local gInterTween = nil
local function ShowPreviewControlPanel(cameraNode, nodeCount,tweenAmount)

end


//****************************************************
//*	Assigns a valid sequence root node to this camera
//* node so that it can fire actor animations
function ENT:AssignActorSequence(sequenceRootNode)


	print("FUNC: AssignActorSequence")
	print("camera: ",self)
	if sequenceRootNode and sequenceRootNode:IsValid() then //TODO: and checktype

		print("data ok")
		for key, Sequence in pairs(self.ActorSequences) do
			if sequenceRootNode == Sequence then
				return
			end
		end
		print("data match not found")

		table.insert(self.ActorSequences, sequenceRootNode)
		print("data inserted: # = ", tostring(#self.ActorSequences))
		datastream.Send("AnimatedCamera_SetActorSequences", {EntIndex = self:EntIndex(), ActorSequences = self.ActorSequences});
			
	end
end

//****************************************************
//*	Retrieves the names of actor sequences for use in the
//* UI
function ENT:GetActorSequenceNames()

	print("FUNC: GetActorSequenceNames()")
	local sequenceNames = {}
	print("actor sequences count = " .. tostring(#self.ActorSequences))
	if self.ActorSequences then
		for key, Sequence in pairs(self.ActorSequences) do
			table.insert(sequenceNames, self.BaseClass.GetName(Sequence))
			print("name added: ".. tostring(sequenceNames))
		end
	end

	return sequenceNames
end

//****************************************************
//*	Removes a sequence root node from this camera
//* given a valid sequence track name
function ENT:RemoveActorSequence(sequenceTrackName)

	if sequenceTrackName then

		local keepSequences = {}

		for key, Sequence in pairs(self.ActorSequences) do
			if Sequence and Sequence:IsValid() then
				print("REM CMP: ",self.BaseClass.GetName(Sequence), " ",sequenceTrackName)
				if self.BaseClass.GetName(Sequence) != sequenceTrackName then
					table.insert(keepSequences, Sequence)
				end
			else
				print("*****ERRROR bad data")
			end
		end	

		self.ActorSequences = keepSequences
		datastream.Send("AnimatedCamera_SetActorSequences", {EntIndex = self:EntIndex(), ActorSequences = self.ActorSequences});
	end
end

function ENT:GetDirectionAngles()
	return (self:GetAngles() + self.ShakeAngles)
end

function ENT:ThinkDirectionAngles()

	self.PrevShakeAngles = self.PrevShakeAngles or Angle(0,0,0)
	self.NextShakeAngles = self.NextShakeAngles or Angle(0,0,0)

	self.ShakeFrequency = self:GetNWFloat(ANICAM_SHAKEFREQUENCY)
	self.ShakeAgression = self:GetNWFloat(ANICAM_SHAKEAGRESSION)

	if (self.ShakeFrequency == 0.0) then
		self.ShakeFrequency = 0.0
	end

	local shakeTime = 1.0 / self.ShakeFrequency

	if self.ShakeTimer > shakeTime then

		local x = math.random(-self.ShakeAgression / 2.0, self.ShakeAgression / 2.0)
		local y = math.random(-self.ShakeAgression / 2.0, self.ShakeAgression / 2.0)
		local z = math.random(-self.ShakeAgression / 2.0, self.ShakeAgression / 2.0)

		self.PrevShakeAngles = self.NextShakeAngles
		self.NextShakeAngles = Angle(x,y,z)
		self.ShakeTimer = 0.0
	else
		self.ShakeTimer = self.ShakeTimer + 0.1
	end

	self.ShakeAngles = self:Lerp(self.PrevShakeAngles, self.NextShakeAngles, self.ShakeTimer / shakeTime)
end

function ENT:StopAllActorSequences()
	
	local currentNode = self.BaseClass.GetRootNode(self)
	local sequenceIndexs = {}

	if currentNode and currentNode:IsValid() then
		
		for key, Sequence in rootNode.ActorSequences do
			table.insert(sequenceIndexs, Sequence:EntIndex())
		end

		currentNode = self.BaseClass.GetNextNode(currentNode)
	end

	datastream.Send("AnimatedCamera_SetActorSequenceStates", {EntIndexs = sequenceIndexs, PlayState = 0});
end

function ENT:SetActorSequencePlayStates(state)
	
	local sequenceEntIdxs = {}
	for key, Sequence in pairs(self.ActorSequences) do
	
		if Sequence and Sequence:IsValid() then
			table.insert(sequenceEntIdxs,Sequence:EntIndex())
		end
	end
	datastream.Send("AnimatedCamera_SetActorSequenceStates", {EntIndexs = sequenceEntIdxs, PlayState = state});
		
end


function ENT:PlaybackData()

	local PlaybackData = {Position = Vector(0,0,0), 
							Angles = Angle(0,0,0), 
							Zoom = 70};

	self.onPlaybackNode = self.onPlaybackNode or self.BaseClass.GetRootNode(self)
	self.onPlaybackNodeTime = self.onPlaybackNodeTime or 0.0
	if self.onPlaybackNode and self.onPlaybackNode:IsValid() then
	
		PlaybackData.onTrack = self.BaseClass.GetName(self)
		PlaybackData.onCamera = self.onPlaybackNode:GetNWInt(ID_NODE_ID)
		PlaybackData.frameType = self.BaseClass.GetRootNode(self):GetNWInt(ANICAM_FRAMETYPE)

		local currentNodeStopDuration = self.onPlaybackNode:GetNWFloat(ANICAM_TIMESTOP)

		//Playing
		self.onPlaybackNodeTime = self.onPlaybackNodeTime + FrameTime()
		if self.onPlaybackNodeTime > currentNodeStopDuration then
			//move onwards
			local nextNode = self.BaseClass.GetNextNode(self.onPlaybackNode)
			if nextNode and nextNode:IsValid() then

				local currentNodeTransDuration = self.onPlaybackNode:GetNWFloat(ANICAM_TIMETRANSITION)
				if currentNodeTransDuration == 0.0 then

					//Move instantly to the next node
					self.onPlaybackNode = nextNode
					self.onPlaybackNode:SetActorSequencePlayStates(1) //Playing
					self.onPlaybackNodeTime = 0.0

					PlaybackData.tween = 0.0
					PlaybackData.Position = self.onPlaybackNode:GetPos()
					PlaybackData.Angles = self.onPlaybackNode:GetDirectionAngles()

				else

					local transitonTime = math.max(0.0, self.onPlaybackNodeTime - currentNodeStopDuration)
					local timeFrac = transitonTime / currentNodeTransDuration
					
					PlaybackData.tween = timeFrac

					if timeFrac < 1.0 then
						self.onPlaybackNode:ThinkDirectionAngles()
						nextNode:ThinkDirectionAngles()

						PlaybackData.Angles = self:Lerp(self.onPlaybackNode:GetDirectionAngles(), nextNode:GetDirectionAngles(), timeFrac)
						PlaybackData.Position = self.BaseClass.EvaluateTrackSegment(self, self.onPlaybackNode, nextNode, timeFrac)
					else

						//Move to the next node
						self.onPlaybackNode = nextNode
						self.onPlaybackNode:SetActorSequencePlayStates(1) //Playing
						self.onPlaybackNodeTime = 0.0
						
						PlaybackData.Position = self.onPlaybackNode:GetPos()
						PlaybackData.Angles = self.onPlaybackNode:GetDirectionAngles()
					end
				end
			else
				self.onPlaybackNode = nil
				self.onPlaybackNodeTime = 0.0
			end
		end

	end
	return PlaybackData

end

function ENT:ThinkCaclPlaybackData()

	local PlaybackData = {position = Vector(0,0,0), 
							angles = Angle(0,0,0), 
							x = 5, y = 5, 
							w = 320, h = 240, 
							fov = 70.0, 
							onCamera = 0, onTrack = "",
							timeCode = "",tween = 0.0,
							frameType = 0};

	
	
	local playFrom = ents.GetByIndex( self.BaseClass.GetRootNode(self):GetNWInt(ANICAM_SHOWCAMERAID) )

	if not (playFrom and playFrom:IsValid()) then
		playFrom = self.BaseClass.GetRootNode(self)
	end

	self.onPreviewNode = self.onPreviewNode or playFrom; //self.BaseClass.GetRootNode(self)
	self.onPreviewNodeTime = self.onPreviewNodeTime or 0.0
	self.previewTimeCode = self.previewTimeCode or 0.0

	if self.onPreviewNode and self.onPreviewNode:IsValid() then
	
		PlaybackData.onTrack = self.BaseClass.GetName(self)
		PlaybackData.onCamera = self.onPreviewNode:GetNWInt(ID_NODE_ID)
		PlaybackData.frameType = self.BaseClass.GetRootNode(self):GetNWInt(ANICAM_FRAMETYPE)

		local currentNodeStopDuration = self.onPreviewNode:GetNWFloat(ANICAM_TIMESTOP)


		//Playing
		if self:GetNWInt(ANICAM_PREVIEWSTATE) == 1 then

			self.onPreviewNodeTime = self.onPreviewNodeTime + FrameTime()
			self.previewTimeCode = self.previewTimeCode + FrameTime()
			local minutes, secondsFrac = math.modf(self.previewTimeCode / 60.0)
			local hours, minutesFrac = math.modf(minutes / 60.0)
			PlaybackData.timeCode = string.format("%2i:%2i:%2.4f", hours, minutes, secondsFrac * 60.0)

			if self.onPreviewNodeTime > currentNodeStopDuration then
				//move onwards
				local nextNode = self.BaseClass.GetNextNode(self.onPreviewNode)
				if nextNode and nextNode:IsValid() then

					local currentNodeTransDuration = self.onPreviewNode:GetNWFloat(ANICAM_TIMETRANSITION)
					if currentNodeTransDuration == 0.0 then

						//Move instantly to the next node
						self.onPreviewNode = nextNode
						self.onPreviewNode:SetActorSequencePlayStates(1) //Playing
						self.onPreviewNodeTime = 0.0

						PlaybackData.tween = 0.0
						PlaybackData.origin = self.onPreviewNode:GetPos()
						PlaybackData.angles = self.onPreviewNode:GetDirectionAngles()
						PlaybackData.fov = self.onPreviewNode:GetNWFloat(ANICAM_ZOOM)
					else

						local transitonTime = math.max(0.0, self.onPreviewNodeTime - currentNodeStopDuration)
						local timeFrac = transitonTime / currentNodeTransDuration
						
						PlaybackData.tween = timeFrac

						if timeFrac < 1.0 then
							self.onPreviewNode:ThinkDirectionAngles()
							nextNode:ThinkDirectionAngles()

							PlaybackData.angles = self:Lerp(self.onPreviewNode:GetDirectionAngles(), nextNode:GetDirectionAngles(), timeFrac)
							PlaybackData.origin = self.BaseClass.EvaluateTrackSegment(self, self.onPreviewNode, nextNode, timeFrac)
							PlaybackData.fov = self:Lerp(self.onPreviewNode:GetNWFloat(ANICAM_ZOOM), nextNode:GetNWFloat(ANICAM_ZOOM), timeFrac)
						else

							//Move to the next node
							self.onPreviewNode = nextNode
							self.onPreviewNode:SetActorSequencePlayStates(1) //Playing
							self.onPreviewNodeTime = 0.0
							
							PlaybackData.origin = self.onPreviewNode:GetPos()
							PlaybackData.angles = self.onPreviewNode:GetDirectionAngles()
							PlaybackData.fov = self.onPreviewNode:GetNWFloat(ANICAM_ZOOM)
						end
					end
				else
					self.previewTimeCode = 0.0
					self.onPreviewNode = nil
					self.onPreviewNodeTime = 0.0
				end
			end

		//Stopped
		elseif self:GetNWInt(ANICAM_PREVIEWSTATE) == 0 then
			self.onPreviewNodeTime = 0.0
			self.onPreviewNode = self.BaseClass.GetRootNode(self)
			PlaybackData.timeCode = "00:00:00.0"
			PlaybackData.tween = 0.0
			PlaybackData.onCamera = 0.0
			self.onPlaybackNode:StopAllActorSequences()
		//Stopped & close window
		elseif self:GetNWInt(ANICAM_PREVIEWSTATE) < 0 then
			//print("CLOSE")
			self.onPlaybackNode:StopAllActorSequences()
			PlaybackData = nil

		//Jump mode
		elseif self:GetNWInt(ANICAM_PREVIEWSTATE) == 3 then

			local cameraNode = ents.GetByIndex(self:GetNWInt(ANICAM_SHOWCAMERAID))
			if cameraNode and cameraNode:IsValid() then

				cameraNode:SetActorSequencePlayStates(1) //Playing
				PlaybackData.origin = cameraNode:GetPos()
				PlaybackData.angles = cameraNode:GetDirectionAngles()
				PlaybackData.fov = cameraNode:GetNWFloat(ANICAM_ZOOM)
				PlaybackData.onCamera = cameraNode:GetNWInt(ID_NODE_ID)
				PlaybackData.tween = 0.0
				
					
				local minutes, secondsFrac = math.modf(self.previewTimeCode / 60.0)
				local hours, minutesFrac = math.modf(minutes / 60.0)
				PlaybackData.timeCode = string.format("%2i:%2i:%2.4f", hours, minutes, secondsFrac * 60.0)
				
			else
				//an error occoured
				PlaybackDataActive = false
			end

		//Pause
		else
				
			local nextNode = self.BaseClass.GetNextNode(self.onPreviewNode)
			if nextNode and nextNode:IsValid() then
		
				local currentNodeTransDuration = self.onPreviewNode:GetNWFloat(ANICAM_TIMETRANSITION)
				local transitonTime = math.max(0.0, self.onPreviewNodeTime - currentNodeStopDuration)
				local timeFrac = transitonTime / currentNodeTransDuration
				PlaybackData.onCamera = self.onPreviewNode:GetNWInt(ID_NODE_ID)
				PlaybackData.tween = timeFrac
					
				local minutes, secondsFrac = math.modf(self.previewTimeCode / 60.0)
				local hours, minutesFrac = math.modf(minutes / 60.0)
				PlaybackData.timeCode = string.format("%2i:%2i:%2.4f", hours, minutes, secondsFrac * 60.0)

				PlaybackData.angles = self:Lerp(self.onPreviewNode:GetDirectionAngles(), nextNode:GetDirectionAngles(), timeFrac)
				PlaybackData.origin = self.BaseClass.EvaluateTrackSegment(self, self.onPreviewNode, nextNode, timeFrac)
				PlaybackData.fov = self:Lerp(self.onPreviewNode:GetNWFloat(ANICAM_ZOOM), nextNode:GetNWFloat(ANICAM_ZOOM), timeFrac)
			end
		end

	end

	return PlaybackData

end

function DrawPreviewScreen()

	if PreviewCameraData then
		surface.SetDrawColor( 0, 0, 0, 150 )
		surface.DrawRect( PreviewCameraData.x - 3, PreviewCameraData.y - 3, PreviewCameraData.w + (2 * 3), PreviewCameraData.h + (2 * 3) + 40 )
		surface.SetDrawColor( 150, 150, 150, 200 )
		surface.DrawOutlinedRect( PreviewCameraData.x - 3, PreviewCameraData.y - 3, PreviewCameraData.w + (2 * 3), PreviewCameraData.h + (2 * 3) + 40)
		
		surface.SetFont("Default")
		surface.SetTextColor( 233, 233, 233, 255 )
		surface.SetTextPos( PreviewCameraData.x + 3, PreviewCameraData.y + PreviewCameraData.h + 3)
		surface.DrawText( string.format( "Track: %s", PreviewCameraData.onTrack))
		surface.SetTextPos( PreviewCameraData.x + 3, PreviewCameraData.y + PreviewCameraData.h + 20)
		surface.DrawText( string.format( "Camera: %i", PreviewCameraData.onCamera))
		
		surface.SetTextPos( PreviewCameraData.x + (PreviewCameraData.w / 2), PreviewCameraData.y + PreviewCameraData.h + 3)
		surface.DrawText( string.format( "Tween: %.2f",  PreviewCameraData.tween))
		surface.SetTextPos( PreviewCameraData.x + (PreviewCameraData.w / 2), PreviewCameraData.y + PreviewCameraData.h + 20)
		surface.DrawText( string.format( "%s",  PreviewCameraData.timeCode))
		render.RenderView( PreviewCameraData )

		
		if PreviewCameraData.frameType == 1 then
			local vertViewSpace = PreviewCameraData.w / (16.0 / 9.0)
			surface.SetDrawColor( 0, 0, 0, 255 )
			surface.DrawRect( PreviewCameraData.x, PreviewCameraData.y, PreviewCameraData.w, (PreviewCameraData.h / 2) - (vertViewSpace / 2))
			surface.DrawRect( PreviewCameraData.x, PreviewCameraData.y + (PreviewCameraData.h / 2) + (vertViewSpace / 2), PreviewCameraData.w, (PreviewCameraData.h / 2) - (vertViewSpace / 2))
		elseif PreviewCameraData.frameType == 2 then
			local vertViewSpace = PreviewCameraData.w / (2.35 / 1.0)
			surface.SetDrawColor( 0, 0, 0, 255 )
			surface.DrawRect( PreviewCameraData.x, PreviewCameraData.y, PreviewCameraData.w, (PreviewCameraData.h / 2) - (vertViewSpace / 2))
			surface.DrawRect( PreviewCameraData.x, PreviewCameraData.y + (PreviewCameraData.h / 2) + (vertViewSpace / 2), PreviewCameraData.w, (PreviewCameraData.h / 2) - (vertViewSpace / 2))
		end

	end
end
hook.Add("HUDPaint", "AnimatedCameraPreviewScreen", DrawPreviewScreen)


function ENT:Think()

	local rootNode = GetRootNode(self)
	if (self == rootNode) then
		if self:GetNWInt(ANICAM_SHOWPREVIEW) == 1 then
			PreviewCameraData = self:ThinkCaclPlaybackData()
		elseif self:GetNWInt(ANICAM_PREVIEWSTATE) < 0 then
			PreviewCameraData = nil
		end		
	end
	
	self:NextThink(CurTime() + 0.5)
end

//****************************************************
//* Renders the direction line from the camera to the
//* direction grip entity
function ENT:DrawDirectionLine(material, thickness)

	if CLIENT then

		local directionGrip = self:GetNWEntity(ANICAM_ROTGRIP)

		if (not directionGrip or not directionGrip:IsValid()) then
			return
		end

		render.SetMaterial(self.BaseClass.Materials["CameraDir"])
		

		local posA = self:GetPos()
		local posB = directionGrip:GetPos()

		local dir = posB - posA;
		
		
		if dir:Length() < 10 then
			dir:Normalize()
			render.StartBeam(2)
			render.AddBeam(posB, thickness, 0.75, Color(255,255,255))
			render.AddBeam(posB +  (dir * 10), thickness, 0, Color(255,255,255))
		else
			dir:Normalize()
			render.StartBeam(3)
			render.AddBeam(posA, thickness, 1, Color(255,255,255))
			render.AddBeam(posB- (dir * 10), thickness, 0.75, Color(255,255,255))
			render.AddBeam(posB , thickness, 0, Color(255,255,255))
		end

		render.EndBeam()
	
	end
end

//****************************************************
//*	Enables the client to retrieve the parameters of 
//* this camera
function ENT:GetParameters()

	local parameters = {}
	parameters.IsSelected		= self:GetNWInt(ANICAM_SELECTED)
	
	parameters.IsShowDirection	= self:GetNWInt(ANICAM_SHOWDIRECTION)
	parameters.ShowFromCameraID	= self:GetNWInt(ANICAM_SHOWCAMERAID)
	parameters.IsShowTangents	= self:GetNWInt(ANICAM_SHOWTANGENTS)

	local rootNode = GetRootNode(self)
	if rootNode == self then
		parameters.IsShowTrack		= self:GetNWInt(ANICAM_SHOWTRACK)
		parameters.ShowPreview		= self:GetNWInt(ANICAM_SHOWPREVIEW)
		parameters.PreviewState		= self:GetNWInt(ANICAM_PREVIEWSTATE)
		parameters.FrameType		= self:GetNWInt(ANICAM_FRAMETYPE)
		parameters.ShowFromCameraID	= self:GetNWInt(ANICAM_SHOWCAMERAID)
		
	else

		local rootParams = rootNode:GetParameters()
		parameters.IsShowTrack		= rootParams.IsShowTrack
		parameters.ShowPreview		= rootParams.ShowPreview
		parameters.PreviewState		= rootParams.PreviewState
		parameters.FrameType		= rootParams.FrameType
		parameters.ShowFromCameraID	= rootParams.ShowFromCameraID
	
	end
	
	parameters.Zoom				= (170 - self:GetNWFloat(ANICAM_ZOOM)) / 1.7
	parameters.ShakeAgression	= self:GetNWFloat(ANICAM_SHAKEAGRESSION)
	parameters.ShakeFrequency	= self:GetNWFloat(ANICAM_SHAKEFREQUENCY)
	parameters.TimeTransition	= self:GetNWFloat(ANICAM_TIMETRANSITION)
	parameters.TimeStop			= self:GetNWFloat(ANICAM_TIMESTOP)

	return parameters
end

//****************************************************
//* Renders the camera node
function ENT:Draw()

	if gDisplayJunk == true then

		self.Entity:DrawModel()

		if LocalPlayer():GetEyeTrace().Entity == self then
			self.BaseClass.DrawTrackNodeData(self)
			self.BaseClass.DrawHighlighted(self)
		end

		if self:GetNWInt(ANICAM_SHOWDIRECTION) == 1 then
			self:DrawDirectionLine(CameraDirMaterial, 10)
		end

		local drawTangents = false
		if self:GetNWInt(ANICAM_SHOWTANGENTS) == 1 then
			drawTangents = true
		end

		self.BaseClass.DrawNumber(self)

		if GetRootNode(self):GetNWInt(ANICAM_SHOWTRACK) == 1 then
			self.BaseClass.DrawTrackSegment(self,self.BaseClass.Materials["CameraPos"],4, drawTangents)
		end
		
	end

end